import json
from collections import defaultdict
from datetime import datetime

import schemas
from ioservice.activity import aggregate_activities_raw
from ioservice.activity_register import aggregate_activity_registers_raw
from utils.dbutil import LookupConfig, get_lookup_stages
from utils.jsonutil import JsonCustomEncoder
from bson import ObjectId


class InstitutionPivot(object):
    """
    机构首页，各月份各机构的活动数量
    """
    month_range = list(range(1, 13))
    sub_class = ["评价和奖励服务处", "综合处", "教育培训服务处", "科技人才服务处"]

    def __init__(self, params: schemas.ActivityStatisticParams):
        self.params = params

    def build_pipeline(self):
        year = self.params.year
        pipeline = [
            {"$match": {
                "start_time": {"$gte": datetime(year, 1, 1), "$lt": datetime(year+1, 1, 1)},
                "is_deleted": {"$ne": True}
            }},
        ]
        lookup_configs = [
            LookupConfig(from_="institution", local_field="institution_id", foreign_field="_id", as_field="institution",
                         fields=["institution_name", "is_deleted"], ref_type="object"),
        ]
        pipeline.extend(get_lookup_stages(lookup_configs))
        pipeline.append({"$match": {"institution.institution_name": {"$in": self.sub_class}}})
        pipeline.append({
            "$group": {
                "_id": {
                    "month": {"$month": "$start_time"},
                    "institution": "$institution.institution_name"
                },
                "count": {"$sum": 1}
            }
        })
        return pipeline

    def warp_agg_result(self, agg_result):
        agg_map = defaultdict(lambda: defaultdict(dict))
        for item in agg_result:
            agg_map[item['_id']['month']][item["_id"]['institution']] = item['count']
        results = []
        for month in self.month_range:
            inst2count = agg_map[month]
            items = []
            for inst in self.sub_class:
                items.append(schemas.AggregateItem(term=inst, count=inst2count.get(inst, 0)))
            results.append(schemas.AggregateResult(name=month, items=items))
        return results


class BusinessPivot(object):
    """
    活动首页，各月份各业务类型活动数量
    """
    month_range = list(range(1, 13))
    sub_class = ["学术", "科普", "智库", "科创", "建家", "国际化"]

    def __init__(self, params: schemas.ActivityStatisticParams):
        self.params = params

    def build_pipeline(self):
        year = self.params.year
        pipeline = [
            {"$match": {
                "start_time": {"$gte": datetime(year, 1, 1), "$lt": datetime(year + 1, 1, 1)},
                "is_deleted": {"$ne": True}
            }},
        ]
        lookup_configs = [
            LookupConfig(from_="business", local_field="business_id", foreign_field="_id", as_field="business",
                         fields=["is_deleted", "first_type"], ref_type="object"),
        ]
        pipeline.extend(get_lookup_stages(lookup_configs))
        pipeline.extend([
            {"$match": {"business.first_type": {"$in": self.sub_class}}},
            {"$project": {"start_time": 1, "first_type": "$business.first_type"}},
            {"$unwind": "$first_type"}
        ])
        pipeline.append({
            "$group": {
                "_id": {
                    "month": {"$month": "$start_time"},
                    "business": "$first_type"
                },
                "count": {"$sum": 1}
            }
        })
        return pipeline

    def warp_agg_result(self, agg_result):
        agg_map = defaultdict(lambda: defaultdict(dict))
        for item in agg_result:
            agg_map[item['_id']['month']][item["_id"]['business']] = item['count']
        results = []
        for month in self.month_range:
            business2count = agg_map[month]
            items = []
            for b in self.sub_class:
                items.append(schemas.AggregateItem(term=b, count=business2count.get(b, 0)))
            results.append(schemas.AggregateResult(name=month, items=items))
        return results


class ActivePersonPivot(object):
    """
    人才首页，活跃人人才
    """
    month_range = list(range(1, 13))

    def __init__(self, params: schemas.ActivityStatisticParams):
        self.params = params

    def build_pipeline(self):
        # TODO: 需求确认中，使用哪个值进行活动参与人数的计算还不确定
        year = self.params.year
        pipeline = [
            {"$match": {
                "start_time": {"$gte": datetime(year, 1, 1), "$lt": datetime(year + 1, 1, 1)},
                "is_deleted": {"$ne": True}, "people_num": {"$gt": 0}
            }},
            {"$group": {
                "_id": {"$month": "$start_time"},
                "count": {"$sum": "$people_num"}
            }}]
        return pipeline

    def warp_agg_result(self, agg_result):
        agg_map = {item['_id']: item['count'] for item in agg_result}
        results = []
        for month in self.month_range:
            results.append(schemas.AggregateResult(name=month, count=agg_map.get(month, 0)))
        return results


pivot_register = {
    schemas.ActivityStatisticPivot.institution: InstitutionPivot,
    schemas.ActivityStatisticPivot.business: BusinessPivot,
    schemas.ActivityStatisticPivot.active_person: ActivePersonPivot
}


async def do_stats_activity(params: schemas.ActivityStatisticParams):
    pivot = pivot_register[params.stats_pivot](params)
    pipeline = pivot.build_pipeline()
    # print(json.dumps(pipeline, ensure_ascii=False, cls=JsonCustomEncoder))
    agg_result = await aggregate_activities_raw(pipeline)
    result = pivot.warp_agg_result(agg_result)
    return result


async def do_stats_activity_register(params: schemas.RegisterRecordStatisticParams):
    # 人才详情页，人才参与活动数量
    pipeline = [
        {"$match": {"person_id": ObjectId(params.person_id), "is_attended": True}},
    ]
    lookup_configs = [
        LookupConfig(from_="activity", local_field="activity_id", foreign_field="_id", as_field="activity",
                     fields=["start_time", "is_deleted"], ref_type="object"),
    ]
    pipeline.extend(get_lookup_stages(lookup_configs))
    pipeline.append({
        "$match": {"activity.start_time": {"$gte": datetime(params.year_range[0], 1, 1), "$lt": datetime(params.year_range[-1]+1, 1, 1)}}
    })
    pipeline.append({
        "$group": {
            "_id": {"$year": "$activity.start_time"},
            "count": {"$sum": 1}
        }
    })
    agg_result = await aggregate_activity_registers_raw(pipeline)
    agg_map = {item['_id']: item['count'] for item in agg_result}
    results = []
    for year in range(params.year_range[0], params.year_range[-1] +1):
        results.append(schemas.AggregateResult(name=year, count=agg_map.get(year, 0)))
    return results
